package com.company.project.util;

import com.company.project.model.Topology;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.ContainerNetwork;
import com.github.dockerjava.api.model.Network;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.*;

@Slf4j
@Component
public class DockerUtil {
    private static final DockerClient dockerClient = init();
    // 定义默认网络名称常量
    private static final String DEFAULT_NETWORK_NAME = "set_network";

    //            DockerClientBuilder.getInstance("tcp://localhost:2375").build();
    private static DockerClient init() {
        String dockerHost = "tcp://localhost:2375";
        DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
                .withDockerHost(dockerHost)
                .build();
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(config.getDockerHost())
                .sslConfig(null)
                .maxConnections(100)
                .connectionTimeout(Duration.ofSeconds(30))
                .responseTimeout(Duration.ofSeconds(60))
                .build();
        DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
//        DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
        dockerClient.infoCmd().exec();
        return dockerClient;
    }

    /**
     * 根据节点类型选择镜像创建容器，并指定网络和IP地址，但不启动
     */
    public static String createContainer(String containerName, String type, String networkName, String ipAddress) {
        if (containerName == null || containerName.isEmpty()) {
            log.error("Container name cannot be null or empty");
            throw new IllegalArgumentException("Container name cannot be null or empty");
        }

        if (type == null || type.isEmpty()) {
            log.error("Container type cannot be null or empty");
            throw new IllegalArgumentException("Container type cannot be null or empty");
        }

        String imageName = getImageByType(type);
        log.info("Creating container with name: {}, type: {}, image: {}, network: {}, IP: {}",
                containerName, type, imageName, networkName, ipAddress);

        try {
            // 检查容器是否已存在
            List<com.github.dockerjava.api.model.Container> containers = dockerClient.listContainersCmd()
                    .withShowAll(true) // 显示所有容器，包括已停止的
                    .exec();

            // 检查是否有同名容器
            Optional<com.github.dockerjava.api.model.Container> existingContainer = containers.stream()
                    .filter(c -> {
                        for (String name : c.getNames()) {
                            if (name.equals("/" + containerName) || name.equals(containerName)) {
                                return true;
                            }
                        }
                        return false;
                    })
                    .findFirst();

            // 如果容器已存在，返回其ID
            if (existingContainer.isPresent()) {
                String containerId = existingContainer.get().getId();
                log.info("Container already exists, using existing container ID: {}", containerId);

                // 如果指定了网络和IP，确保已存在的容器连接到该网络
                if (networkName != null && !networkName.isEmpty() && ipAddress != null && !ipAddress.isEmpty()) {
                    try {
                        connectToNetwork(containerId, networkName, ipAddress);
                    } catch (Exception e) {
                        log.warn("Failed to connect existing container to network: {}", e.getMessage());
                        // 不抛出异常，继续使用已存在的容器
                    }
                }

                return containerId;
            }

            // 如果指定了网络，确保网络存在
            String networkId = null;
            if (networkName != null && !networkName.isEmpty()) {
                networkId = ensureNetworkExists(networkName);
            }

            // 创建新容器
            log.info("Creating new container with name: {}", containerName);
            CreateContainerResponse container;

            // 为攻击者容器添加特权
            if ("attacker".equals(type)) {
                container = dockerClient.createContainerCmd(imageName)
                        .withName(containerName)
                        .withCmd("/bin/sh", "-c", "tail -f /dev/null") // 添加一个持续运行的命令
                        .withTty(true) // 分配一个伪终端
                        .withStdinOpen(true) // 保持STDIN打开
                        .withPrivileged(true) // 以特权模式运行容器
                        .withCapAdd(com.github.dockerjava.api.model.Capability.NET_ADMIN,
                                com.github.dockerjava.api.model.Capability.NET_RAW)
                        .withNetworkMode(DEFAULT_NETWORK_NAME)
                        .exec();
            } else {
                container = dockerClient.createContainerCmd(imageName)
                        .withName(containerName)
                        .withCmd("/bin/sh", "-c", "tail -f /dev/null") // 添加一个持续运行的命令
                        .withTty(true) // 分配一个伪终端
                        .withStdinOpen(true) // 保持STDIN打开
                        .withNetworkMode(DEFAULT_NETWORK_NAME)
                        .exec();
            }

            if (container == null) {
                log.error("Failed to create container, response is null");
                throw new RuntimeException("Failed to create container, response is null");
            }

            String containerId = container.getId();
            if (containerId == null || containerId.isEmpty()) {
                log.error("Created container has null or empty ID");
                throw new RuntimeException("Created container has null or empty ID");
            }

            // 如果指定了网络和IP，将容器连接到网络
            if (networkName != null && !networkName.isEmpty() && ipAddress != null && !ipAddress.isEmpty()) {
                connectToNetwork(containerId, networkName, ipAddress);
            }
            log.info("Container created successfully, ID: {}", containerId);
            return containerId;
        } catch (Exception e) {
            log.error("Error creating container: {}", e.getMessage(), e);
            throw new RuntimeException("Failed to create container: " + e.getMessage(), e);
        }
    }

    /**
     * 确保网络存在，如果不存在则创建，返回网络ID
     */
    private static String ensureNetworkExists(String networkName) {
        // 使用固定的子网
        String subnet = "192.168.1.0/24";

        Optional<Network> existingNetwork = dockerClient.listNetworksCmd()
                .exec()
                .stream()
                .filter(net -> net.getName().equals(networkName))
                .findFirst();

        if (!existingNetwork.isPresent()) {
            log.info("Creating network {} with subnet {}", networkName, subnet);
            String networkId = dockerClient.createNetworkCmd()
                    .withName(networkName)
                    .withIpam(new Network.Ipam().withConfig(
                            new Network.Ipam.Config()
                                    .withSubnet(subnet)
                    ))
                    .exec()
                    .getId();
            log.info("Network created with ID: {}", networkId);
            return networkId;
        } else {
            String networkId = existingNetwork.get().getId();
            log.info("Network {} already exists with ID: {}", networkName, networkId);
            return networkId;
        }
    }

    /**
     * 确保默认网络 set_network 存在，如果不存在则创建
     */
    private static String ensureSetNetworkExists() {
        // 使用固定的子网
        String subnet = "192.168.1.0/24";

        Optional<Network> existingNetwork = dockerClient.listNetworksCmd()
                .exec()
                .stream()
                .filter(net -> net.getName().equals(DEFAULT_NETWORK_NAME))
                .findFirst();

        if (!existingNetwork.isPresent()) {
            log.info("Creating default network {} with subnet {}", DEFAULT_NETWORK_NAME, subnet);
            String networkId = dockerClient.createNetworkCmd()
                    .withName(DEFAULT_NETWORK_NAME)
                    .withIpam(new Network.Ipam().withConfig(
                            new Network.Ipam.Config()
                                    .withSubnet(subnet)
                    ))
                    .exec()
                    .getId();
            log.info("Default network created with ID: {}", networkId);
            return networkId;
        } else {
            String networkId = existingNetwork.get().getId();
            log.info("Default network {} already exists with ID: {}", DEFAULT_NETWORK_NAME, networkId);
            return networkId;
        }
    }

    /**
     * 启动容器
     */
    public static void startContainer(String containerId) {
        if (containerId == null || containerId.isEmpty()) {
            log.error("Container ID cannot be null or empty");
            throw new IllegalArgumentException("Container ID cannot be null or empty");
        }

        try {
            log.info("Starting container: {}", containerId);
            dockerClient.startContainerCmd(containerId).exec();
            log.info("Container started successfully: {}", containerId);
        } catch (Exception e) {
            log.error("Error starting container {}: {}", containerId, e.getMessage(), e);
            throw new RuntimeException("Failed to start container: " + e.getMessage(), e);
        }
    }

    /**
     * 批量启动容器
     */
    public static void startContainers(Collection<String> containerIds) {
        if (containerIds == null || containerIds.isEmpty()) {
            log.error("Container ID list is null or empty");
            throw new IllegalArgumentException("Container ID list is null or empty");
        }

        log.info("Starting {} containers", containerIds.size());
        List<String> failedContainers = new ArrayList<>();

        for (String containerId : containerIds) {
            try {
                startContainer(containerId);
            } catch (Exception e) {
                log.error("Error starting container {}: {}", containerId, e.getMessage(), e);
                failedContainers.add(containerId);
            }
        }

        if (!failedContainers.isEmpty()) {
            log.error("Failed to start {} containers", failedContainers.size());
            throw new RuntimeException("Failed to start containers: " + String.join(", ", failedContainers));
        }

        log.info("Successfully started {} containers", containerIds.size());
    }


    /**
     * 获取节点类型对应的镜像
     */
    private static String getImageByType(String type) {
        switch (type) {
            case "attacker":
                return "attacker-image:latest"; // 攻击节点镜像
            case "server":
                return "server-image:latest";  // 服务器节点镜像
            default:
                throw new IllegalArgumentException("Unknown node type: " + type);
        }
    }

    /**
     * 将容器连接到指定网络，并分配 IP 地址
     */
    public static void connectToNetwork(String containerId, String networkName, String ipAddress) {
        // 使用固定的子网，而不是动态计算
        String subnet = "192.168.1.0/24";

        // 修正网络存在性检查逻辑
        Optional<Network> existingNetwork = dockerClient.listNetworksCmd()
                .exec()
                .stream()
                .filter(net -> net.getName().equals(networkName))
                .findFirst();

        if (!existingNetwork.isPresent()) { // 修正判断条件
            log.info("Creating network {} with subnet {}", networkName, subnet);
            dockerClient.createNetworkCmd()
                    .withName(networkName)
                    .withIpam(new Network.Ipam().withConfig(
                            new Network.Ipam.Config()
                                    .withSubnet(subnet) // 使用固定的子网
                    ))
                    .exec();
            // 重新获取新建的网络信息
            existingNetwork = dockerClient.listNetworksCmd()
                    .exec()
                    .stream()
                    .filter(net -> net.getName().equals(networkName))
                    .findFirst();
        }

        // 连接容器到网络（新增IP格式校验）
        validateIPFormat(ipAddress, subnet);
        log.info("Connecting container {} to network {} with IP {}", containerId, networkName, ipAddress);
        dockerClient.connectToNetworkCmd()
                .withContainerId(containerId)
                .withContainerNetwork(new ContainerNetwork().withIpv4Address(ipAddress))
                .withNetworkId(existingNetwork.orElseThrow(() ->
                        new RuntimeException("网络创建失败: " + networkName)).getId())
                .exec();
        log.info("Container {} connected to network {} with IP {}", containerId, networkName, ipAddress);
    }

    // 新增IP地址校验
    private static void validateIPFormat(String ipAddress, String subnet) {
        if (!ipAddress.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$")) {
            throw new IllegalArgumentException("非法IP地址格式: " + ipAddress);
        }

        // 从子网中提取前缀（例如从192.168.1.0/24提取192.168.1）
        String subnetPrefix = subnet.split("\\.")[0] + "." + subnet.split("\\.")[1] + "." + subnet.split("\\.")[2];
        String ipPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1] + "." + ipAddress.split("\\.")[2];

        if (!ipPrefix.equals(subnetPrefix)) {
            throw new IllegalArgumentException("IP地址不在子网范围内: " + ipAddress + "，预期子网前缀: " + subnetPrefix);
        }

        // 检查IP地址的最后一个部分是否在有效范围内（1-254）
        int lastOctet = Integer.parseInt(ipAddress.split("\\.")[3]);
        if (lastOctet < 1 || lastOctet > 254) {
            throw new IllegalArgumentException("IP地址的最后一个部分必须在1-254范围内: " + ipAddress);
        }

        log.info("IP地址 {} 验证通过，在子网 {} 范围内", ipAddress, subnet);
    }

    /**
     * 批量将容器连接到网络，然后启动容器
     */
    public static void connectContainersToNetwork(Map<String, String> containerIdMap, String networkName, Map<String, String> ipAddressMap) {
        log.info("Connecting {} containers to network {}", containerIdMap.size(), networkName);

        // 首先检查IP地址映射是否完整
        for (String containerName : containerIdMap.keySet()) {
            if (!ipAddressMap.containsKey(containerName)) {
                throw new IllegalArgumentException("容器 " + containerName + " 没有对应的IP地址配置");
            }
        }

        // 记录所有IP地址，用于检查重复
        Set<String> usedIPs = new HashSet<>();

        // 连接容器到网络
        for (Map.Entry<String, String> entry : containerIdMap.entrySet()) {
            String containerName = entry.getKey();
            String containerId = entry.getValue();
            String ipAddress = ipAddressMap.get(containerName);

            // 检查IP地址是否重复
            if (usedIPs.contains(ipAddress)) {
                log.warn("IP地址 {} 已被分配给其他容器，可能导致网络冲突", ipAddress);
            }
            usedIPs.add(ipAddress);

            try {
                log.info("Connecting container {} (ID: {}) to network {} with IP {}",
                        containerName, containerId, networkName, ipAddress);
                connectToNetwork(containerId, networkName, ipAddress);
                log.info("Successfully connected container {} to network", containerName);
            } catch (Exception e) {
                log.error("Failed to connect container {} to network: {}", containerName, e.getMessage());
                throw new RuntimeException("Failed to connect container " + containerName + " to network: " + e.getMessage(), e);
            }
        }

        log.info("Successfully connected {} containers to network {}", containerIdMap.size(), networkName);

        // 所有容器连接到网络后，启动容器
        log.info("Starting all containers after network connection");
        try {
            startContainers(containerIdMap.values());
            log.info("All containers started successfully after network connection");
        } catch (Exception e) {
            log.error("Failed to start containers after network connection: {}", e.getMessage());
            throw new RuntimeException("Failed to start containers after network connection: " + e.getMessage(), e);
        }
    }

    /**
     * 删除单个容器
     */
    public static void removeContainer(String containerId) {
        dockerClient.removeContainerCmd(containerId).withForce(true).exec();
    }

    /**
     * 批量删除容器
     */
    public static void removeContainers(List<String> containerIds) {
        for (String containerId : containerIds) {
            removeContainer(containerId);
        }
    }

    /**
     * 批量创建容器，但不指定网络和IP地址，也不启动
     */
//    public static Map<String, String> createContainers(List<Topology.Node> nodes) {
//        if (nodes == null || nodes.isEmpty()) {
//            log.error("Node list is null or empty");
//            throw new IllegalArgumentException("Node list is null or empty");
//        }
//
//        log.info("Creating {} containers without network configuration", nodes.size());
//        Map<String, String> containerIdMap = new HashMap<>();
//        List<String> failedContainers = new ArrayList<>();
//
//        for (Topology.Node node : nodes) {
//            try {
//                String containerId = createContainer(node.getName(), node.getType());
//                if (containerId == null || containerId.isEmpty()) {
//                    log.error("Failed to create container for node: {}", node.getName());
//                    failedContainers.add(node.getName());
//                    continue;
//                }
//                containerIdMap.put(node.getName(), containerId);
//                log.info("Container created: name={}, type={}, id={}", node.getName(), node.getType(), containerId);
//            } catch (Exception e) {
//                log.error("Error creating container for node {}: {}", node.getName(), e.getMessage(), e);
//                failedContainers.add(node.getName());
//            }
//        }
//
//        if (!failedContainers.isEmpty()) {
//            log.error("Failed to create {} containers: {}", failedContainers.size(), failedContainers);
//            throw new RuntimeException("Failed to create containers: " + String.join(", ", failedContainers));
//        }
//
//        log.info("Successfully created {} containers", containerIdMap.size());
//        return containerIdMap;
//    }

    /**
     * 批量创建容器，直接使用默认网络和节点中的IP地址
     */
    public static Map<String, String> createContainers(List<Topology.Node> nodes) {
        if (nodes == null || nodes.isEmpty()) {
            log.error("Node list is null or empty");
            throw new IllegalArgumentException("Node list is null or empty");
        }

        // 确保默认网络存在
        String networkId = ensureSetNetworkExists();

        log.info("Creating {} containers with default network {}", nodes.size(), DEFAULT_NETWORK_NAME);
        Map<String, String> containerIdMap = new HashMap<>();
        List<String> failedContainers = new ArrayList<>();

        // 记录所有IP地址，用于检查重复
        Set<String> usedIPs = new HashSet<>();

        for (Topology.Node node : nodes) {
            String ipAddress = node.getIp();

            // 检查IP地址是否为空
            if (ipAddress == null || ipAddress.isEmpty()) {
                log.error("Node {} does not have an IP address", node.getName());
                failedContainers.add(node.getName());
                continue;
            }

            // 检查IP地址是否重复
            if (usedIPs.contains(ipAddress)) {
                log.warn("IP地址 {} 已被分配给其他容器，可能导致网络冲突", ipAddress);
            }
            usedIPs.add(ipAddress);

            try {
                // 直接创建带有网络配置的容器
                log.info("Creating container with network: name={}, type={}, network={}, ip={}",
                        node.getName(), node.getType(), DEFAULT_NETWORK_NAME, ipAddress);

                String containerId = createContainer(node.getName(), node.getType(), DEFAULT_NETWORK_NAME, ipAddress);
                if (containerId == null || containerId.isEmpty()) {
                    log.error("Failed to create container for node: {}", node.getName());
                    failedContainers.add(node.getName());
                    continue;
                }
                containerIdMap.put(node.getName(), containerId);
                log.info("Container created with network: name={}, type={}, id={}, network={}, ip={}",
                        node.getName(), node.getType(), containerId, DEFAULT_NETWORK_NAME, ipAddress);
            } catch (Exception e) {
                log.error("Error creating container for node {}: {}", node.getName(), e.getMessage(), e);
                failedContainers.add(node.getName());
            }
        }

        if (!failedContainers.isEmpty()) {
            log.error("Failed to create {} containers: {}", failedContainers.size(), failedContainers);
            throw new RuntimeException("Failed to create containers: " + String.join(", ", failedContainers));
        }

        log.info("Successfully created {} containers with network {}", containerIdMap.size(), DEFAULT_NETWORK_NAME);
        return containerIdMap;
    }
}